/*
 ============================================================================
 Name        : main.c
 Author      : Galli, Nadia
 Pivetta, Agustin
 Tkaczyszyn, Facundo
 Version     : 1.0
 Description : Tp0 66.20: infraestructura basica
 ============================================================================
 */

//#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <getopt.h>
#include <math.h>

/*
 * Imprimo la ayuda
 */
void printHelp() {
  printf(
"Usage:\n"
"  tp0 -h\n"
"  tp0 -V\n"
"  tp0 [options]\n"
"Options:\n"
"  -h, --help		Print this information.\n"
"  -V, --version		Print version and quit.\n"
"  -b, --binary		Generate a output file consisting of ’1’s and ’0’s.\n"
"  -r, --reverse		Generate a output file using an encoded file as" 
" imput (default mode).\n"
"  -i, --input		Path to input file (defaults to stdin).\n"
"  -o, --output		Path to output file (defaults to stdout).\n");
}

/*
 * Imprimo la version
 */
void printVersion() {
  printf("66.20 - TP0: infraestructura basica, Version 1.0\n");
  ;
  return;
}

/*
 * Checkeo de errores en archivos
 */
void checkIOError(FILE* file2check, char* error_msg, FILE* otherFile) {
  if (ferror(file2check)) {
    // si fallo escribo por stderr el mensaje
    fprintf(stderr, "%s", error_msg);

    // si los archivos sobre los que estoy escribiendo no son stdin ni stdout
    // los cierro antes de salir
    if (file2check != stdin && file2check != stdout) {
      fclose(file2check);
    }

    if (otherFile != stdin && otherFile != stdout) {
      fclose(otherFile);
    }

    // retorno el codigo de error
    exit(-1);
  }
}

/*
 * Abro los archivos de input/output
 */
FILE* openFile(char* fileName, FILE* default_file, const char* mode) {
  FILE* fd;

  if (fileName == NULL) {
    fd = default_file;
  } else {
    fd = fopen(fileName, mode);
  }

  if (!fd) {
    fprintf(stderr, "Error al intentar abrir el archivo\n");
    exit(-1);
  }

  return fd;
}

int traducirBin2Tex(FILE* input, FILE* output) {
  int i;
  size_t nbytes;
  char letra;
  double aux;
  char num[8];

  nbytes = fread(num, sizeof(char), 8, input);
  checkIOError(input, "Se produjo un error al leer del archivo de entrada",
      output);
  if (nbytes < 8 && nbytes > 0) {
  // Lei menos que 8 bytes, error de archivo de entrada
    fprintf(stderr, "Error de lectura, el archivo finaliza de forma inesperada\n");
    exit(-1);
  }

  while (!feof(input)) {

    letra = 0;
    for (i = 0; i <= 7; i++) {
      if (num[i] == '1') {
        aux = pow(2, 7 - i);
        letra = letra + (unsigned char) aux;

      } else if (num[i] != '0') {  //No es ni 1 ni 0.
        fprintf(stderr, "Error, el caracter leido no es un valor binario\n");
        exit(-1);
      }
    }
    fprintf(output, "%c", letra);
    checkIOError(output, "Se produjo un error al leer del archivo de entrada",
        input);

    nbytes = fread(num, sizeof(char), 8, input);
    checkIOError(input, "Se produjo un error al leer del archivo de entrada",
        output);
    if (nbytes < 8 && nbytes > 0) {
    // Lei menos que 8 bytes, error de archivo de entrada
      fprintf(stderr, "Error de lectura, el archivo finaliza de forma inesperada");
      exit(-1);
    }
  }
  return 0;
}

int traducirTex2Bin(FILE* input, FILE* output) {
  unsigned char resul;
  unsigned char letra;
  int i;
  unsigned char binario[8];

  letra = fgetc(input);
  checkIOError(input, "Se produjo un error al leer del archivo de entrada",
      output);

  while (!feof(input)) {
    for (i = 7; i >= 0; i--) {
      resul = letra % 2;
      letra = letra / 2;
      binario[i] = resul;
    }
    for (i = 0; i <= 7; i++) {
      if (binario[i] == 0) {
        binario[i] = '0';
      } else {
        binario[i] = '1';
      }
      fprintf(output, "%c", binario[i]);
      checkIOError(output, "Se produjo un error al leer del archivo de entrada",
          input);
    }

    letra = fgetc(input);
    checkIOError(input, "Se produjo un error al leer del archivo de entrada",
        output);
  }
  return 0;
}

int main(int argc, char** argv) {
  // Fuerzo que un error en getopt no se imprima por stderr
  opterr = 0;
  // Indica cuando termine de procesar todos los argumentos
  int nextOpt = 0;
  // Cadena que lista las opciones cortas validas
  const char* const opCortas = "hVbri:o:";
  // Estructura de structs describiendo los valores largos
  const struct option opLargas[] = { { "help", no_argument, NULL, 'h' }, {
      "version", no_argument, NULL, 'V' }, { "binary", no_argument, NULL, 'b' },
      { "reverse", no_argument, NULL, 'r' }, { "input", optional_argument, NULL,
          'i' }, { "output", optional_argument, NULL, 'o' }, { NULL,
          no_argument, NULL, 0 } };

  // Variable donde recibo el nombre del archivo de entrada
  char* inputFName = NULL;
  // Variable donde recibo el nombre del archivo de salida
  char* outputFName = NULL;
  // Variable donde guardo el modo de ejecucion 
  //(1: binary mode | 2: reverse mode)
  char mode = 1;  //Defaul reverse mode

  FILE* input;
  FILE* output;

  while (1) {
    //Leo que me devuelve get_opt
    nextOpt = getopt_long(argc, argv, opCortas, opLargas, NULL);

    if (nextOpt == -1) {
      break;
    }

    switch (nextOpt) {
    case 'h': {
      printHelp();
      return 0;
      break;
    }

    case 'V': {
      printVersion();
      return 0;
      break;
    }

    case 'b': {
      mode = 1;
      break;
    }

    case 'r': {
      mode = 2;
      break;
    }

    case 'i': {
      if (strcmp(optarg, "-") != 0) {
        inputFName = malloc(strlen(optarg) + 1);
        strcpy(inputFName, optarg);
      }
      break;
    }

    case 'o': {
      if (strcmp(optarg, "-") != 0) {
        outputFName = malloc(strlen(optarg) + 1);
        strcpy(outputFName, optarg);
      }
      break;
    }

    default: {
      printHelp();
      exit(-1);
      break;
    }
    }
  }

  if (mode == 1) {
    input = openFile(inputFName, stdin, "r");
    output = openFile(outputFName, stdout, "w");
    traducirBin2Tex(input, output);
  } else if (mode == 2) {
    input = openFile(inputFName, stdin, "rb");
    output = openFile(outputFName, stdout, "wb");
    traducirTex2Bin(input, output);
  }

  fclose(input);
  fclose(output);

  return 0;
}
